1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package sun.font;
27
28 import java.lang.ref.SoftReference;
29 import java.lang.ref.WeakReference;
30 import java.awt.Font;
31 import java.awt.GraphicsEnvironment;
32 import java.awt.Rectangle;
33 import java.awt.geom.AffineTransform;
34 import java.awt.geom.GeneralPath;
35 import java.awt.geom.NoninvertibleTransformException;
36 import java.awt.geom.Point2D;
37 import java.awt.geom.Rectangle2D;
38 import java.util.concurrent.ConcurrentHashMap;
39 import static sun.awt.SunHints.*;
40
41
42 public class FileFontStrike extends PhysicalStrike {
43
44
45
46
47 static final int INVISIBLE_GLYPHS = 0x0fffe;
48
49 private FileFont fileFont;
50
51
52
53
54
55 private static final int UNINITIALISED = 0;
56 private static final int INTARRAY = 1;
57 private static final int LONGARRAY = 2;
58 private static final int SEGINTARRAY = 3;
59 private static final int SEGLONGARRAY = 4;
60
61 private volatile int glyphCacheFormat = UNINITIALISED;
62
63
64 private static final int SEGSHIFT = 5;
65 private static final int SEGSIZE = 1 << SEGSHIFT;
66
67 private boolean segmentedCache;
68 private int[][] segIntGlyphImages;
69 private long[][] segLongGlyphImages;
70
71
72
73
74
75
76
77
78
79
80
81
82 private float[] horizontalAdvances;
83 private float[][] segHorizontalAdvances;
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 ConcurrentHashMap<Integer, Rectangle2D.Float> boundsMap;
102 SoftReference<ConcurrentHashMap<Integer, Point2D.Float>>
103 glyphMetricsMapRef;
104
105 AffineTransform invertDevTx;
106
107 boolean useNatives;
108 NativeStrike[] nativeStrikes;
109
110
111 private int intPtSize;
112
113
114 private static native boolean initNative();
115 private static boolean isXPorLater = false;
116 static {
117 if (FontUtilities.isWindows && !FontUtilities.useT2K &&
118 !GraphicsEnvironment.isHeadless()) {
119 isXPorLater = initNative();
120 }
121 }
122
123 FileFontStrike(FileFont fileFont, FontStrikeDesc desc) {
124 super(fileFont, desc);
125 this.fileFont = fileFont;
126
127 if (desc.style != fileFont.style) {
128
129
130
131
132 if ((desc.style & Font.ITALIC) == Font.ITALIC &&
133 (fileFont.style & Font.ITALIC) == 0) {
134 algoStyle = true;
135 italic = 0.7f;
136 }
137 if ((desc.style & Font.BOLD) == Font.BOLD &&
138 ((fileFont.style & Font.BOLD) == 0)) {
139 algoStyle = true;
140 boldness = 1.33f;
141 }
142 }
143 double[] matrix = new double[4];
144 AffineTransform at = desc.glyphTx;
145 at.getMatrix(matrix);
146 if (!desc.devTx.isIdentity() &&
147 desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
148 try {
149 invertDevTx = desc.devTx.createInverse();
150 } catch (NoninvertibleTransformException e) {
151 }
152 }
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 boolean disableHinting = desc.aaHint != INTVAL_TEXT_ANTIALIAS_OFF &&
169 fileFont.familyName.startsWith("Amble");
170
171
172
173
174
175
176
177 if (Double.isNaN(matrix[0]) || Double.isNaN(matrix[1]) ||
178 Double.isNaN(matrix[2]) || Double.isNaN(matrix[3]) ||
179 fileFont.getScaler() == null) {
180 pScalerContext = NullFontScaler.getNullScalerContext();
181 } else {
182 pScalerContext = fileFont.getScaler().createScalerContext(matrix,
183 desc.aaHint, desc.fmHint,
184 boldness, italic, disableHinting);
185 }
186
187 mapper = fileFont.getMapper();
188 int numGlyphs = mapper.getNumGlyphs();
189
190
191
192
193
194
195
196
197
198 float ptSize = (float)matrix[3];
199 int iSize = intPtSize = (int)ptSize;
200 boolean isSimpleTx = (at.getType() & complexTX) == 0;
201 segmentedCache =
202 (numGlyphs > SEGSIZE << 3) ||
203 ((numGlyphs > SEGSIZE << 1) &&
204 (!isSimpleTx || ptSize != iSize || iSize < 6 || iSize > 36));
205
206
207
208
209
210
211
212
213 if (pScalerContext == 0L) {
214
215
216
217 this.disposer = new FontStrikeDisposer(fileFont, desc);
218 initGlyphCache();
219 pScalerContext = NullFontScaler.getNullScalerContext();
220 SunFontManager.getInstance().deRegisterBadFont(fileFont);
221 return;
222 }
223
224
225
226
227
228
229
230 if (FontUtilities.isWindows && isXPorLater &&
231 !FontUtilities.useT2K &&
232 !GraphicsEnvironment.isHeadless() &&
233 !fileFont.useJavaRasterizer &&
234 (desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
235 desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR) &&
236 (matrix[1] == 0.0 && matrix[2] == 0.0 &&
237 matrix[0] == matrix[3] &&
238 matrix[0] >= 3.0 && matrix[0] <= 100.0) &&
239 !((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize)) {
240 useNatives = true;
241 }
242 else if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) {
243
244
245 if (matrix[1] == 0.0 && matrix[2] == 0.0 &&
246 matrix[0] >= 6.0 && matrix[0] <= 36.0 &&
247 matrix[0] == matrix[3]) {
248 useNatives = true;
249 int numNatives = fileFont.nativeFonts.length;
250 nativeStrikes = new NativeStrike[numNatives];
251
252
253
254 for (int i=0; i<numNatives; i++) {
255 nativeStrikes[i] =
256 new NativeStrike(fileFont.nativeFonts[i], desc, false);
257 }
258 }
259 }
260 if (FontUtilities.isLogging() && FontUtilities.isWindows) {
261 FontUtilities.getLogger().info
262 ("Strike for " + fileFont + " at size = " + intPtSize +
263 " use natives = " + useNatives +
264 " useJavaRasteriser = " + fileFont.useJavaRasterizer +
265 " AAHint = " + desc.aaHint +
266 " Has Embedded bitmaps = " +
267 ((TrueTypeFont)fileFont).
268 useEmbeddedBitmapsForSize(intPtSize));
269 }
270 this.disposer = new FontStrikeDisposer(fileFont, desc, pScalerContext);
271
272
273
274
275
276
277
278 double maxSz = 48.0;
279 getImageWithAdvance =
280 Math.abs(at.getScaleX()) <= maxSz &&
281 Math.abs(at.getScaleY()) <= maxSz &&
282 Math.abs(at.getShearX()) <= maxSz &&
283 Math.abs(at.getShearY()) <= maxSz;
284
285
286
287
288
289
290
291
292
293
294 if (!getImageWithAdvance) {
295 if (!segmentedCache) {
296 horizontalAdvances = new float[numGlyphs];
297
298 for (int i=0; i<numGlyphs; i++) {
299 horizontalAdvances[i] = Float.MAX_VALUE;
300 }
301 } else {
302 int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;
303 segHorizontalAdvances = new float[numSegments][];
304 }
305 }
306 }
307
308
309
310
311
312 public int getNumGlyphs() {
313 return fileFont.getNumGlyphs();
314 }
315
316 long getGlyphImageFromNative(int glyphCode) {
317 if (FontUtilities.isWindows) {
318 return getGlyphImageFromWindows(glyphCode);
319 } else {
320 return getGlyphImageFromX11(glyphCode);
321 }
322 }
323
324
325
326
327 private native long _getGlyphImageFromWindows(String family,
328 int style,
329 int size,
330 int glyphCode,
331 boolean fracMetrics);
332
333 long getGlyphImageFromWindows(int glyphCode) {
334 String family = fileFont.getFamilyName(null);
335 int style = desc.style & Font.BOLD | desc.style & Font.ITALIC
336 | fileFont.getStyle();
337 int size = intPtSize;
338 long ptr = _getGlyphImageFromWindows
339 (family, style, size, glyphCode,
340 desc.fmHint == INTVAL_FRACTIONALMETRICS_ON);
341 if (ptr != 0) {
342
343
344
345
346
347
348
349 float advance = getGlyphAdvance(glyphCode, false);
350 StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset,
351 advance);
352 return ptr;
353 } else {
354 return fileFont.getGlyphImage(pScalerContext, glyphCode);
355 }
356 }
357
358
359 long getGlyphImageFromX11(int glyphCode) {
360 long glyphPtr;
361 char charCode = fileFont.glyphToCharMap[glyphCode];
362 for (int i=0;i<nativeStrikes.length;i++) {
363 CharToGlyphMapper mapper = fileFont.nativeFonts[i].getMapper();
364 int gc = mapper.charToGlyph(charCode)&0xffff;
365 if (gc != mapper.getMissingGlyphCode()) {
366 glyphPtr = nativeStrikes[i].getGlyphImagePtrNoCache(gc);
367 if (glyphPtr != 0L) {
368 return glyphPtr;
369 }
370 }
371 }
372 return fileFont.getGlyphImage(pScalerContext, glyphCode);
373 }
374
375 long getGlyphImagePtr(int glyphCode) {
376 if (glyphCode >= INVISIBLE_GLYPHS) {
377 return StrikeCache.invisibleGlyphPtr;
378 }
379 long glyphPtr = 0L;
380 if ((glyphPtr = getCachedGlyphPtr(glyphCode)) != 0L) {
381 return glyphPtr;
382 } else {
383 if (useNatives) {
384 glyphPtr = getGlyphImageFromNative(glyphCode);
385 if (glyphPtr == 0L && FontUtilities.isLogging()) {
386 FontUtilities.getLogger().info
387 ("Strike for " + fileFont +
388 " at size = " + intPtSize +
389 " couldn't get native glyph for code = " + glyphCode);
390 }
391 } if (glyphPtr == 0L) {
392 glyphPtr = fileFont.getGlyphImage(pScalerContext,
393 glyphCode);
394 }
395 return setCachedGlyphPtr(glyphCode, glyphPtr);
396 }
397 }
398
399 void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
400
401 for (int i=0; i<len; i++) {
402 int glyphCode = glyphCodes[i];
403 if (glyphCode >= INVISIBLE_GLYPHS) {
404 images[i] = StrikeCache.invisibleGlyphPtr;
405 continue;
406 } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
407 continue;
408 } else {
409 long glyphPtr = 0L;
410 if (useNatives) {
411 glyphPtr = getGlyphImageFromNative(glyphCode);
412 } if (glyphPtr == 0L) {
413 glyphPtr = fileFont.getGlyphImage(pScalerContext,
414 glyphCode);
415 }
416 images[i] = setCachedGlyphPtr(glyphCode, glyphPtr);
417 }
418 }
419 }
420
421
422
423 private static final int SLOTZEROMAX = 0xffffff;
424 int getSlot0GlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
425
426 int convertedCnt = 0;
427
428 for (int i=0; i<len; i++) {
429 int glyphCode = glyphCodes[i];
430 if (glyphCode >= SLOTZEROMAX) {
431 return convertedCnt;
432 } else {
433 convertedCnt++;
434 }
435 if (glyphCode >= INVISIBLE_GLYPHS) {
436 images[i] = StrikeCache.invisibleGlyphPtr;
437 continue;
438 } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
439 continue;
440 } else {
441 long glyphPtr = 0L;
442 if (useNatives) {
443 glyphPtr = getGlyphImageFromNative(glyphCode);
444 }
445 if (glyphPtr == 0L) {
446 glyphPtr = fileFont.getGlyphImage(pScalerContext,
447 glyphCode);
448 }
449 images[i] = setCachedGlyphPtr(glyphCode, glyphPtr);
450 }
451 }
452 return convertedCnt;
453 }
454
455
456 long getCachedGlyphPtr(int glyphCode) {
457 switch (glyphCacheFormat) {
458 case INTARRAY:
459 return intGlyphImages[glyphCode] & INTMASK;
460 case SEGINTARRAY:
461 int segIndex = glyphCode >> SEGSHIFT;
462 if (segIntGlyphImages[segIndex] != null) {
463 int subIndex = glyphCode % SEGSIZE;
464 return segIntGlyphImages[segIndex][subIndex] & INTMASK;
465 } else {
466 return 0L;
467 }
468 case LONGARRAY:
469 return longGlyphImages[glyphCode];
470 case SEGLONGARRAY:
471 segIndex = glyphCode >> SEGSHIFT;
472 if (segLongGlyphImages[segIndex] != null) {
473 int subIndex = glyphCode % SEGSIZE;
474 return segLongGlyphImages[segIndex][subIndex];
475 } else {
476 return 0L;
477 }
478 }
479
480 return 0L;
481 }
482
483 private synchronized long setCachedGlyphPtr(int glyphCode, long glyphPtr) {
484 switch (glyphCacheFormat) {
485 case INTARRAY:
486 if (intGlyphImages[glyphCode] == 0) {
487 intGlyphImages[glyphCode] = (int)glyphPtr;
488 return glyphPtr;
489 } else {
490 StrikeCache.freeIntPointer((int)glyphPtr);
491 return intGlyphImages[glyphCode] & INTMASK;
492 }
493
494 case SEGINTARRAY:
495 int segIndex = glyphCode >> SEGSHIFT;
496 int subIndex = glyphCode % SEGSIZE;
497 if (segIntGlyphImages[segIndex] == null) {
498 segIntGlyphImages[segIndex] = new int[SEGSIZE];
499 }
500 if (segIntGlyphImages[segIndex][subIndex] == 0) {
501 segIntGlyphImages[segIndex][subIndex] = (int)glyphPtr;
502 return glyphPtr;
503 } else {
504 StrikeCache.freeIntPointer((int)glyphPtr);
505 return segIntGlyphImages[segIndex][subIndex] & INTMASK;
506 }
507
508 case LONGARRAY:
509 if (longGlyphImages[glyphCode] == 0L) {
510 longGlyphImages[glyphCode] = glyphPtr;
511 return glyphPtr;
512 } else {
513 StrikeCache.freeLongPointer(glyphPtr);
514 return longGlyphImages[glyphCode];
515 }
516
517 case SEGLONGARRAY:
518 segIndex = glyphCode >> SEGSHIFT;
519 subIndex = glyphCode % SEGSIZE;
520 if (segLongGlyphImages[segIndex] == null) {
521 segLongGlyphImages[segIndex] = new long[SEGSIZE];
522 }
523 if (segLongGlyphImages[segIndex][subIndex] == 0L) {
524 segLongGlyphImages[segIndex][subIndex] = glyphPtr;
525 return glyphPtr;
526 } else {
527 StrikeCache.freeLongPointer(glyphPtr);
528 return segLongGlyphImages[segIndex][subIndex];
529 }
530 }
531
532
533
534
535
536 initGlyphCache();
537 return setCachedGlyphPtr(glyphCode, glyphPtr);
538 }
539
540
541 private synchronized void initGlyphCache() {
542
543 int numGlyphs = mapper.getNumGlyphs();
544 int tmpFormat = UNINITIALISED;
545 if (segmentedCache) {
546 int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;
547 if (longAddresses) {
548 tmpFormat = SEGLONGARRAY;
549 segLongGlyphImages = new long[numSegments][];
550 this.disposer.segLongGlyphImages = segLongGlyphImages;
551 } else {
552 tmpFormat = SEGINTARRAY;
553 segIntGlyphImages = new int[numSegments][];
554 this.disposer.segIntGlyphImages = segIntGlyphImages;
555 }
556 } else {
557 if (longAddresses) {
558 tmpFormat = LONGARRAY;
559 longGlyphImages = new long[numGlyphs];
560 this.disposer.longGlyphImages = longGlyphImages;
561 } else {
562 tmpFormat = INTARRAY;
563 intGlyphImages = new int[numGlyphs];
564 this.disposer.intGlyphImages = intGlyphImages;
565 }
566 }
567 glyphCacheFormat = tmpFormat;
568 }
569
570 float getGlyphAdvance(int glyphCode) {
571 return getGlyphAdvance(glyphCode, true);
572 }
573
574
575
576
577
578
579 private float getGlyphAdvance(int glyphCode, boolean getUserAdv) {
580 float advance;
581
582 if (glyphCode >= INVISIBLE_GLYPHS) {
583 return 0f;
584 }
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613 if (horizontalAdvances != null) {
614 advance = horizontalAdvances[glyphCode];
615 if (advance != Float.MAX_VALUE) {
616 if (!getUserAdv && invertDevTx != null) {
617 Point2D.Float metrics = new Point2D.Float(advance, 0f);
618 desc.devTx.deltaTransform(metrics, metrics);
619 return metrics.x;
620 } else {
621 return advance;
622 }
623 }
624 } else if (segmentedCache && segHorizontalAdvances != null) {
625 int segIndex = glyphCode >> SEGSHIFT;
626 float[] subArray = segHorizontalAdvances[segIndex];
627 if (subArray != null) {
628 advance = subArray[glyphCode % SEGSIZE];
629 if (advance != Float.MAX_VALUE) {
630 if (!getUserAdv && invertDevTx != null) {
631 Point2D.Float metrics = new Point2D.Float(advance, 0f);
632 desc.devTx.deltaTransform(metrics, metrics);
633 return metrics.x;
634 } else {
635 return advance;
636 }
637 }
638 }
639 }
640
641 if (!getUserAdv && invertDevTx != null) {
642 Point2D.Float metrics = new Point2D.Float();
643 fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
644 return metrics.x;
645 }
646
647 if (invertDevTx != null || !getUserAdv) {
648
649
650
651 advance = getGlyphMetrics(glyphCode, getUserAdv).x;
652 } else {
653 long glyphPtr;
654 if (getImageWithAdvance) {
655
656
657
658
659
660 glyphPtr = getGlyphImagePtr(glyphCode);
661 } else {
662 glyphPtr = getCachedGlyphPtr(glyphCode);
663 }
664 if (glyphPtr != 0L) {
665 advance = StrikeCache.unsafe.getFloat
666 (glyphPtr + StrikeCache.xAdvanceOffset);
667
668 } else {
669 advance = fileFont.getGlyphAdvance(pScalerContext, glyphCode);
670 }
671 }
672
673 if (horizontalAdvances != null) {
674 horizontalAdvances[glyphCode] = advance;
675 } else if (segmentedCache && segHorizontalAdvances != null) {
676 int segIndex = glyphCode >> SEGSHIFT;
677 int subIndex = glyphCode % SEGSIZE;
678 if (segHorizontalAdvances[segIndex] == null) {
679 segHorizontalAdvances[segIndex] = new float[SEGSIZE];
680 for (int i=0; i<SEGSIZE; i++) {
681 segHorizontalAdvances[segIndex][i] = Float.MAX_VALUE;
682 }
683 }
684 segHorizontalAdvances[segIndex][subIndex] = advance;
685 }
686 return advance;
687 }
688
689 float getCodePointAdvance(int cp) {
690 return getGlyphAdvance(mapper.charToGlyph(cp));
691 }
692
693
694
695
696 void getGlyphImageBounds(int glyphCode, Point2D.Float pt,
697 Rectangle result) {
698
699 long ptr = getGlyphImagePtr(glyphCode);
700 float topLeftX, topLeftY;
701
702
703
704
705 if (ptr == 0L) {
706 result.x = (int) Math.floor(pt.x);
707 result.y = (int) Math.floor(pt.y);
708 result.width = result.height = 0;
709 return;
710 }
711
712 topLeftX = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftXOffset);
713 topLeftY = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftYOffset);
714
715 result.x = (int)Math.floor(pt.x + topLeftX);
716 result.y = (int)Math.floor(pt.y + topLeftY);
717 result.width =
718 StrikeCache.unsafe.getShort(ptr+StrikeCache.widthOffset) &0x0ffff;
719 result.height =
720 StrikeCache.unsafe.getShort(ptr+StrikeCache.heightOffset) &0x0ffff;
721
722
723
724
725
726
727
728 if ((desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
729 desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR)
730 && topLeftX <= -2.0f) {
731 int minx = getGlyphImageMinX(ptr, (int)result.x);
732 if (minx > result.x) {
733 result.x += 1;
734 result.width -=1;
735 }
736 }
737 }
738
739 private int getGlyphImageMinX(long ptr, int origMinX) {
740
741 int width = StrikeCache.unsafe.getChar(ptr+StrikeCache.widthOffset);
742 int height = StrikeCache.unsafe.getChar(ptr+StrikeCache.heightOffset);
743 int rowBytes =
744 StrikeCache.unsafe.getChar(ptr+StrikeCache.rowBytesOffset);
745
746 if (rowBytes == width) {
747 return origMinX;
748 }
749
750 long pixelData;
751 if (StrikeCache.nativeAddressSize == 4) {
752 pixelData = 0xffffffff &
753 StrikeCache.unsafe.getInt(ptr + StrikeCache.pixelDataOffset);
754 } else {
755 pixelData =
756 StrikeCache.unsafe.getLong(ptr + StrikeCache.pixelDataOffset);
757 }
758 if (pixelData == 0L) {
759 return origMinX;
760 }
761
762 for (int y=0;y<height;y++) {
763 for (int x=0;x<3;x++) {
764 if (StrikeCache.unsafe.getByte(pixelData+y*rowBytes+x) != 0) {
765 return origMinX;
766 }
767 }
768 }
769 return origMinX+1;
770 }
771
772
773
774
775 StrikeMetrics getFontMetrics() {
776 if (strikeMetrics == null) {
777 strikeMetrics =
778 fileFont.getFontMetrics(pScalerContext);
779 if (invertDevTx != null) {
780 strikeMetrics.convertToUserSpace(invertDevTx);
781 }
782 }
783 return strikeMetrics;
784 }
785
786 Point2D.Float getGlyphMetrics(int glyphCode) {
787 return getGlyphMetrics(glyphCode, true);
788 }
789
790 private Point2D.Float getGlyphMetrics(int glyphCode, boolean getImage) {
791 Point2D.Float metrics = new Point2D.Float();
792
793
794 if (glyphCode >= INVISIBLE_GLYPHS) {
795 return metrics;
796 }
797 long glyphPtr;
798 if (getImageWithAdvance && getImage) {
799
800
801
802
803
804 glyphPtr = getGlyphImagePtr(glyphCode);
805 } else {
806 glyphPtr = getCachedGlyphPtr(glyphCode);
807 }
808 if (glyphPtr != 0L) {
809 metrics = new Point2D.Float();
810 metrics.x = StrikeCache.unsafe.getFloat
811 (glyphPtr + StrikeCache.xAdvanceOffset);
812 metrics.y = StrikeCache.unsafe.getFloat
813 (glyphPtr + StrikeCache.yAdvanceOffset);
814
815
816
817 if (invertDevTx != null) {
818 invertDevTx.deltaTransform(metrics, metrics);
819 }
820 } else {
821
822
823
824
825
826
827
828 Integer key = Integer.valueOf(glyphCode);
829 Point2D.Float value = null;
830 ConcurrentHashMap<Integer, Point2D.Float> glyphMetricsMap = null;
831 if (glyphMetricsMapRef != null) {
832 glyphMetricsMap = glyphMetricsMapRef.get();
833 }
834 if (glyphMetricsMap != null) {
835 value = glyphMetricsMap.get(key);
836 if (value != null) {
837 metrics.x = value.x;
838 metrics.y = value.y;
839
840 return metrics;
841 }
842 }
843 if (value == null) {
844 fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
845
846
847
848 if (invertDevTx != null) {
849 invertDevTx.deltaTransform(metrics, metrics);
850 }
851 value = new Point2D.Float(metrics.x, metrics.y);
852
853
854
855 if (glyphMetricsMap == null) {
856 glyphMetricsMap =
857 new ConcurrentHashMap<Integer, Point2D.Float>();
858 glyphMetricsMapRef =
859 new SoftReference<ConcurrentHashMap<Integer,
860 Point2D.Float>>(glyphMetricsMap);
861 }
862 glyphMetricsMap.put(key, value);
863 }
864 }
865 return metrics;
866 }
867
868 Point2D.Float getCharMetrics(char ch) {
869 return getGlyphMetrics(mapper.charToGlyph(ch));
870 }
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889 Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
890
891 if (boundsMap == null) {
892 boundsMap = new ConcurrentHashMap<Integer, Rectangle2D.Float>();
893 }
894
895 Integer key = Integer.valueOf(glyphCode);
896 Rectangle2D.Float bounds = boundsMap.get(key);
897
898 if (bounds == null) {
899 bounds = fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
900 boundsMap.put(key, bounds);
901 }
902 return bounds;
903 }
904
905 public Rectangle2D getOutlineBounds(int glyphCode) {
906 return fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
907 }
908
909 private
910 WeakReference<ConcurrentHashMap<Integer,GeneralPath>> outlineMapRef;
911
912 GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
913
914 GeneralPath gp = null;
915 ConcurrentHashMap<Integer, GeneralPath> outlineMap = null;
916
917 if (outlineMapRef != null) {
918 outlineMap = outlineMapRef.get();
919 if (outlineMap != null) {
920 gp = (GeneralPath)outlineMap.get(glyphCode);
921 }
922 }
923
924 if (gp == null) {
925 gp = fileFont.getGlyphOutline(pScalerContext, glyphCode, 0, 0);
926 if (outlineMap == null) {
927 outlineMap = new ConcurrentHashMap<Integer, GeneralPath>();
928 outlineMapRef =
929 new WeakReference
930 <ConcurrentHashMap<Integer,GeneralPath>>(outlineMap);
931 }
932 outlineMap.put(glyphCode, gp);
933 }
934 gp = (GeneralPath)gp.clone();
935 if (x != 0f || y != 0f) {
936 gp.transform(AffineTransform.getTranslateInstance(x, y));
937 }
938 return gp;
939 }
940
941 GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
942 return fileFont.getGlyphVectorOutline(pScalerContext,
943 glyphs, glyphs.length, x, y);
944 }
945
946 protected void adjustPoint(Point2D.Float pt) {
947 if (invertDevTx != null) {
948 invertDevTx.deltaTransform(pt, pt);
949 }
950 }
951 }